package com.shengqin.controller.api;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.shengqin.common.pojo.CommonPage;
import com.shengqin.common.pojo.CommonResult;
import com.shengqin.config.CommonException;
import com.shengqin.controller.api.dto.*;
import com.shengqin.controller.dawei.dto.FeatureResp;
import com.shengqin.controller.dawei.dto.PicInfo;
import com.shengqin.entity.*;
import com.shengqin.pojo.exam.CheckInParam;
import com.shengqin.pojo.plan.result.ImportTestResultParam;
import com.shengqin.service.*;
import com.shengqin.service.impl.FaceFeatureService;
import com.shengqin.utils.GradeUtil;
import io.swagger.v3.oas.annotations.Operation;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

@RestController
@Slf4j
public class ApiController {

    @Resource
    private IPlanService planService;
    @Resource
    private IPlanStudentService planStudentService;
    @Resource
    private ITestResultService testResultService;
    @Resource
    private ISchoolService schoolService;
    @Resource
    private IStudentService studentService;
    @Resource
    private ISummaryResultService summaryResultService;
    @Resource
    private IPlanApplyProjectService applyProjectService;
    @Value("${upload.path}")
    private String uploadPath;
    @Resource
    private IFileService fileService;
    @Resource
    private IResultRecordService resultRecordService;
    @Resource
    private FaceFeatureService faceFeatureService;
    @Resource
    private AppReleaseService appReleaseService;

    @GetMapping("/api/app/release")
    public AppRelease getAppRelease() throws IOException {
        return appReleaseService.getById(1);
    }

    @PostMapping("/api/face/getFeature")
    public FeatureResp fileUpload(@RequestBody PicInfoReq picInfoReq) throws IOException {
        if(picInfoReq.getPic_info() == null || !StringUtils.hasLength(picInfoReq.getPic_info().getBase64())){
            throw new CommonException("请先上传图片数据");
        }
        return faceFeatureService.getFaceFaceture(picInfoReq.getPic_info().getBase64());
    }

    @PostMapping("/api/file/upload")
    public CommonResult<String> fileUpload(@RequestPart("file") MultipartFile file) throws IOException {
        File outFile = new File(uploadPath + "/avatar/" + file.getOriginalFilename());
        if (outFile.exists()) {
            outFile.delete();
        }
        Files.copy(file.getInputStream(), Paths.get(uploadPath + "/avatar/" + file.getOriginalFilename()));
        String url = fileService.storageFileWithReturnUrl("avatar", file);
        log.info("上传文件url,{}", url);
        return CommonResult.data(url);
    }

    @Operation(description = "集体检录")
    @PostMapping("api/plan/checkInGroup")
    public CommonResult<List<PlanApplyProject>> checkInGroup(@RequestBody CheckInParam checkInParam) {
        return CommonResult.data(applyProjectService.checkInGroup(checkInParam));
    }

    @Operation(description = "单个检录")
    @PostMapping("api/plan/checkIn")
    public CommonResult<List<PlanApplyProject>> checkIn(@RequestBody CheckInParam checkInParam) {
        return CommonResult.data(applyProjectService.checkIn(checkInParam));
    }

    @PostMapping("api/plan/result/page")
    public CommonResult<Page<TestResult>> getPage(@RequestBody CommonPage<TestResult> queryPage) {
        return CommonResult.data(testResultService.getPage(queryPage));
    }

    @PostMapping("api/plan/queryResultByStudyCode")
    public CommonResult<List<TestResult>> queryResultByStudyCode(@RequestBody TestResult params) {
        if (params.getPlanId() == null) {
            throw new CommonException("计划ID不能为空");
        }
        if (!StringUtils.hasLength(params.getStudyCode())) {
            throw new CommonException("学号不能为空");
        }
        PlanStudent planStudent = planStudentService.queryByPlanIdAndStudyCode(params.getStudyCode(), params.getPlanId());
        if (planStudent == null) {
            throw new CommonException("未查询到你的信息，请联系管理员");
        }
        return CommonResult.data(testResultService.listByPlanIdAndStudyCode(params.getStudyCode(), params.getPlanId()));
    }

    @PostMapping("api/plan/result/listByStdInfo")
    public CommonResult<List<PlanTestResultModel>> listByStdInfo(@RequestBody PlanTestApiParam testApiParam) {
        QueryWrapper<SummaryResult> wrapper = new QueryWrapper<>();
        if (!StringUtils.hasLength(testApiParam.getStudyCode()) && !StringUtils.hasLength(testApiParam.getIdCardNo())) {
            throw new CommonException("学籍号和身份证号必须输入一个");
        }
        Student student;
        if (StringUtils.hasLength(testApiParam.getStudyCode())) {
            student = studentService.getByIdNumber(testApiParam.getIdCardNo());
            wrapper.lambda().eq(SummaryResult::getStudyCode, testApiParam.getStudyCode());
        } else {
            if (StringUtils.hasLength(testApiParam.getIdCardNo())) {
                student = studentService.getByIdNumber(testApiParam.getIdCardNo());
                if (student == null) {
                    throw new CommonException("未查询到学生数据");
                }
                if (StringUtils.hasLength(student.getStudyCode())) {
                    wrapper.lambda().eq(SummaryResult::getStudyCode, testApiParam.getStudyCode());
                }
            } else {
                student = null;
            }
        }

        List<SummaryResult> testSummaryResultList = summaryResultService.list(wrapper);
        if (CollectionUtils.isEmpty(testSummaryResultList)) {
            throw new CommonException("未查询到数据");
        }
        Map<Integer, SummaryResult> testResultMap = testSummaryResultList.stream().collect(Collectors.toMap(SummaryResult::getPlanId, summaryResult -> summaryResult));
        List<Plan> planList = planService.listByIds(testResultMap.keySet());
        Map<Integer, Plan> planMap = planList.stream().collect(Collectors.toMap(Plan::getId, p -> p));
        List<PlanTestResultModel> testResultModelList = new ArrayList<>();
        testResultMap.forEach((planId, summaryResult) -> {
            Plan plan = planMap.getOrDefault(planId, null);
            if (plan != null) {
                PlanTestResultModel planTestResultModel = new PlanTestResultModel();
                planTestResultModel.setPlanId(plan.getId());
                planTestResultModel.setPlanName(plan.getName());
                if (student != null) {
                    planTestResultModel.setName(student.getName());
                    planTestResultModel.setStudyCode(student.getStudyCode());
                    planTestResultModel.setGradeName(GradeUtil.getGradeName(student.getGradeId()));
                    planTestResultModel.setClassesName(student.getClassesName());
                    planTestResultModel.setTotalScore(summaryResult.getTotalScore());
                    planTestResultModel.setTotalLevel(summaryResult.getTotalLevel());
                }
                List<TestResult> testResultList = summaryResult.getTestResultList();
                if (!CollectionUtils.isEmpty(testResultList)) {
                    List<TestResultModel> testResultModels = new ArrayList<>();
                    testResultList.forEach(item -> {
                        TestResultModel testResultModel = new TestResultModel();
                        testResultModel.setResult(item.getResult());
                        testResultModel.setLevel(item.getLevel());
                        testResultModel.setScore(item.getScore());
                        testResultModel.setProjectName(item.getPrjName());
                        testResultModel.setTestEndTime(item.getTestEndTime());
                        testResultModel.setTestStartTime(item.getTestStartTime());
                        testResultModels.add(testResultModel);
                    });
                    planTestResultModel.setTestResultList(testResultModels);
                }
                testResultModelList.add(planTestResultModel);
            }
        });
        if (!CollectionUtils.isEmpty(testResultModelList)) {
            testResultModelList.sort((a, b) -> b.getPlanId() - a.getPlanId());
        }
        return CommonResult.data(testResultModelList);
    }

    @PostMapping("/api/testresult/upload")
    public CommonResult uploadResult(@RequestBody TestResultUploadParam resultUploadParam) {
//        log.info("上传成绩入参:{}", JSON.toJSONString(resultUploadParam));
        Plan plan = planService.getByCode(resultUploadParam.getPlanId());
        if (plan == null) {
            throw new CommonException("未查询到该计划编码，请检查");
        }
        if (plan.getState().equals(3)) {
            throw new CommonException("计划已结束，暂时无法上传成绩");
        }
        CompletableFuture.runAsync(() -> buildTestResults(resultUploadParam, plan));
        List<ImportTestResultParam> cachedDataList = new ArrayList<>();
        if (!CollectionUtils.isEmpty(resultUploadParam.getScoreList())) {
            List<UploadScoreItem> scoreItems = resultUploadParam.getScoreList();
            Set<String> studyCodes = new HashSet<>();
            resultUploadParam.getScoreList().forEach(score -> studyCodes.add(score.getStudyCode()));
            List<PlanApplyProject> applyProjectList = applyProjectService.queryByPlanIdAndStudyCodes(plan.getId(), studyCodes);
            if (plan.getExamFlag()) {
                Map<String, List<PlanApplyProject>> studyApplyProjectMap = applyProjectList.stream().collect(Collectors.groupingBy(PlanApplyProject::getStudyCode));
                scoreItems.removeIf(item -> {
                    List<PlanApplyProject> planApplyProjects = studyApplyProjectMap.getOrDefault(item.getStudyCode(), null);
                    if (CollectionUtils.isEmpty(planApplyProjects)) {
                        return true;
                    }
                    PlanApplyProject planApplyProject = planApplyProjects.get(0);
                    if (!StringUtils.hasLength(planApplyProject.getProjectNames())) {
                        return true;
                    }
                    boolean containProjectName = planApplyProject.getProjectNames().contains(item.getPrjName());
                    return !containProjectName;
                });
            }
            if (!CollectionUtils.isEmpty(scoreItems)) {
                if (resultUploadParam.getForce() != null && !resultUploadParam.getForce()) {
                    if (!CollectionUtils.isEmpty(applyProjectList)) {
                        List<PlanApplyProject> lockedList = applyProjectList.stream().filter(item -> item.getLocked() != null && item.getLocked()).collect(Collectors.toList());
                        Set<String> lockedStudyCodes = lockedList.stream().map(PlanApplyProject::getStudyCode).collect(Collectors.toSet());
                        scoreItems.removeIf(scoreItem -> lockedStudyCodes.contains(scoreItem.getStudyCode()));
                    }
                }
                scoreItems.forEach(uploadScoreItem -> {
                    ImportTestResultParam importTestResultParam = new ImportTestResultParam();
                    BeanUtils.copyProperties(uploadScoreItem, importTestResultParam);
                    cachedDataList.add(importTestResultParam);
                });
                if (!CollectionUtils.isEmpty(cachedDataList)) {
                    CompletableFuture.runAsync(() -> testResultService.importBatch(cachedDataList, plan.getId(), "device"));
                }
            }
        }
        return CommonResult.data(true).setMsg("上传成绩成功");
    }

    private void buildTestResults(TestResultUploadParam resultUploadParam, Plan plan) {
        try {
            processResultRecords(resultUploadParam, plan);
        } catch (Exception e) {
            log.error("保存成绩记录报错", e);
        }
    }

    private void processResultRecords(TestResultUploadParam resultUploadParam, Plan plan) {
        List<ResultRecord> resultRecords = buildResultRecords(resultUploadParam, plan.getId());
        if (!CollectionUtils.isEmpty(resultRecords)) {
            resultRecordService.saveBatch(resultRecords);
        }
    }

    private List<ResultRecord> buildResultRecords(TestResultUploadParam testResultUploadParam, Integer planId) {
        return testResultUploadParam.getScoreList().stream().map(scoreItem -> {
            ResultRecord resultRecord = new ResultRecord();
            BeanUtils.copyProperties(scoreItem, resultRecord);
            resultRecord.setPlanId(planId);
            resultRecord.setUploadType(StringUtils.hasLength(scoreItem.getDeviceNo()) ? "device" : testResultUploadParam.getUploadType());
            resultRecord.setCreateTime(LocalDateTime.now());
            return resultRecord;
        }).collect(Collectors.toList());
    }

    @GetMapping("/api/testPlan/student")
    public CommonResult<List<PlanStudent>> getPage(ApiPlanPage apiPlanPage) {

        if (apiPlanPage.getPlanId() == null) {
            return CommonResult.data(new ArrayList<>());
        }
        Plan plan = planService.getByCode(apiPlanPage.getPlanId());
        if(plan == null){
            throw new CommonException("计划不存在,请检查计划编码");
        }
//        if(plan!=null && !plan.getState().equals(2)){
//            throw new CommonException("计划未开始，无法查询");
//        }
        QueryWrapper<PlanStudent> wrapper = new QueryWrapper<>();
        wrapper.lambda().eq(PlanStudent::getPlanId, plan.getId());
        List<String> orders = new ArrayList<>();
        orders.add("grade_id");
        orders.add("classes_name");
        orders.add("study_code");
        wrapper.orderByAsc(orders);
        Page<PlanStudent> resultPage = new Page<>(apiPlanPage.getPageNo(), apiPlanPage.getPageSize());
        planStudentService.page(resultPage, wrapper);

        if (!CollectionUtils.isEmpty(resultPage.getRecords())) {
            Set<Integer> schoolIds = new HashSet<>();
            resultPage.getRecords().forEach(rec -> {
                rec.setImg(rec.getAvatarUrl());
                schoolIds.add(rec.getSchoolId());
            });
            List<School> schoolList = schoolService.listByIds(schoolIds);
            Map<Integer, School> schoolMap = schoolList.stream().collect(Collectors.toMap(School::getId, s -> s));
            resultPage.getRecords().forEach(rec -> {
                rec.setSchoolName(schoolMap.getOrDefault(rec.getSchoolId(), new School()).getName());
            });
        }

        if (CollectionUtils.isEmpty(resultPage.getRecords())) {
            return CommonResult.data(new ArrayList<>());
        }
        return CommonResult.data(resultPage.getRecords());
    }

    @GetMapping("/api/testPlan/queryStudentPage")
    public CommonResult<Page<PlanStudent>> queryStudentPage(ApiPlanPage apiPlanPage) {

        if (apiPlanPage.getPlanId() == null) {
            return CommonResult.data(new Page<>());
        }
        Plan plan = planService.getByCode(apiPlanPage.getPlanId());
        if(plan == null){
            throw new CommonException("计划不存在,请检查计划编码");
        }
        QueryWrapper<PlanStudent> wrapper = new QueryWrapper<>();
        wrapper.lambda().eq(PlanStudent::getPlanId, plan.getId());
        if(StringUtils.hasLength(apiPlanPage.getStudyCode())){
            wrapper.lambda().eq(PlanStudent::getStudyCode, apiPlanPage.getStudyCode());
        }
        if(apiPlanPage.getSchoolId() != null){
            wrapper.lambda().eq(PlanStudent::getSchoolId, apiPlanPage.getSchoolId());
        }
        if(apiPlanPage.getGradeId() != null){
            wrapper.lambda().eq(PlanStudent::getGradeId, apiPlanPage.getGradeId());
        }
        List<String> orders = new ArrayList<>();
        orders.add("grade_id");
        orders.add("classes_name");
        orders.add("study_code");
        wrapper.orderByAsc(orders);
        Page<PlanStudent> resultPage = new Page<>(apiPlanPage.getPageNo(), apiPlanPage.getPageSize());
        planStudentService.page(resultPage, wrapper);

        if (!CollectionUtils.isEmpty(resultPage.getRecords())) {
            Set<Integer> schoolIds = new HashSet<>();
            Set<String> studyCodes = new HashSet<>();
            resultPage.getRecords().forEach(rec -> {
                rec.setImg(rec.getAvatarUrl());
                studyCodes.add(rec.getStudyCode());
                schoolIds.add(rec.getSchoolId());
            });
            List<FaceFeature> faceFeatures = faceFeatureService.listByStudyCodes(studyCodes);
            Map<String,FaceFeature> faceFeatureMap = faceFeatures.stream().collect(Collectors.toMap(FaceFeature::getStudyCode, f -> f));
            List<School> schoolList = schoolService.listByIds(schoolIds);
            Map<Integer, School> schoolMap = schoolList.stream().collect(Collectors.toMap(School::getId, s -> s));
            resultPage.getRecords().forEach(rec -> {
                rec.setSchoolName(schoolMap.getOrDefault(rec.getSchoolId(), new School()).getName());
                FaceFeature faceFeature = faceFeatureMap.getOrDefault(rec.getStudyCode(),null);
                if (faceFeature != null) {
                    rec.setFaceData(StringUtils.hasLength(faceFeature.getFaceData()) ? faceFeature.getFaceData() : "");
                    rec.setFeatureUrl(StringUtils.hasLength(faceFeature.getFeatureUrl()) ? faceFeature.getFeatureUrl() : "");
                    rec.setFeatureVersion(StringUtils.hasLength(faceFeature.getFeatureVersion()) ? faceFeature.getFeatureVersion() : "");
                } else {
                    rec.setFaceData("");
                    rec.setFeatureUrl("");
                    rec.setFeatureVersion("");
                }
            });
        }

        return CommonResult.data(resultPage);
    }


    @GetMapping("/api/student/sync")
    public CommonResult<List<StdInfoModel>> syncPage(DaWeiStdQueryParam daWeiStdQueryParam) {
        QueryWrapper<Student> wrapper = new QueryWrapper<>();
        if (StringUtils.hasLength(daWeiStdQueryParam.getGradeId())) {
            wrapper.lambda().eq(Student::getGradeId, daWeiStdQueryParam.getGradeId());
        }

        if (StringUtils.hasLength(daWeiStdQueryParam.getStudyCode())) {
            wrapper.lambda().eq(Student::getStudyCode, daWeiStdQueryParam.getStudyCode());
        }

        if (StringUtils.hasLength(daWeiStdQueryParam.getClassesCode())) {
            wrapper.lambda().eq(Student::getClassesCode, daWeiStdQueryParam.getClassesCode());
        }

        if (StringUtils.hasLength(daWeiStdQueryParam.getSex())) {
            wrapper.lambda().eq(Student::getSex, daWeiStdQueryParam.getSex());
        }

        if (StringUtils.hasLength(daWeiStdQueryParam.getName())) {
            wrapper.lambda().eq(Student::getName, daWeiStdQueryParam.getName());
        }

        List<String> orders = new ArrayList<>();
        orders.add("grade_id");
        orders.add("classes_name");
        orders.add("study_code");
        wrapper.orderByAsc(orders);

        List<Student> studentList = studentService.list(wrapper);
        List<StdInfoModel> stdInfoModels = new ArrayList<>();
        if (!CollectionUtils.isEmpty(studentList)) {
            Set<Integer> schoolIds = studentList.stream().map(Student::getSchoolId).collect(Collectors.toSet());
            List<School> schoolList = schoolService.listByIds(schoolIds);
            Map<Integer, School> schoolMap = schoolList.stream().collect(Collectors.toMap(School::getId, s -> s));

            studentList.forEach(student -> {
                StdInfoModel stdInfoModel = new StdInfoModel();
                School school = schoolMap.getOrDefault(student.getSchoolId(), new School());
                student.setGradeName(GradeUtil.getGradeName(student.getGradeId()));
                if(school != null){
                    student.setSchoolName(school.getName());
                }
                BeanUtils.copyProperties(student,stdInfoModel);
                stdInfoModels.add(stdInfoModel);
            });
        }

        return CommonResult.data(stdInfoModels);
    }

}
